/*
MIT License

Copyright (c) 2021 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,

https://bmstu.codes/lsx/simodo
*/

#include "simodo/interpret/Interpret.h"
#include "simodo/interpret/AnalyzeException.h"

#include "simodo/loom/Loom_interface.h"
#include "simodo/bormental/DrBormental.h"
#include "simodo/inout/convert/functions.h"

#include <algorithm>
#include <cassert>

#define SemanticFiber_DEBUG_no

namespace simodo::interpret
{
    Interpret::Interpret(
                    InterpretType type,
                    inout::Reporter_abstract & m, 
                    loom::Loom_interface & loom, 
                    inout::uri_set_t files, 
                    const ast::Node & code,
                    SemanticModules hosts,
                    bool stop_by_error)
        : _type(type)
        , _m(m)
        , _loom(loom)
        , _files(files)
        , _hosts(hosts)
        , _stop_by_error(stop_by_error)
    {
        for(auto host : _hosts) 
            host->setInterpret(this);

        /// \note PVS Studio err fixed: V1053 Calling the 'addFlowLayer' virtual function in the 
        /// constructor may lead to unexpected result at runtime.

        addFlowLayer_internal(code, UNDEFINED_BOUNDARY_INDEX, nullptr);
    }

    Interpret::Interpret(
                    InterpretType type,
                    inout::Reporter_abstract & m, 
                    loom::Loom_interface & loom, 
                    std::vector<std::shared_ptr<SemanticModuleFactory_interface>> semantic_factories,
                    bool stop_by_error)
        : _type(type)
        , _m(m)
        , _loom(loom)
        , _semantic_factories(semantic_factories)
        , _stop_by_error(stop_by_error)
    {
        for(auto f : _semantic_factories) {
            SemanticModule_interface * s = f->create(*this);
            if (s) {
                s->setInterpret(this);
                _hosts.push_back(s);
            }
        }
    }

    Interpret::~Interpret()
    {
        for(auto h : _hosts)
            delete h;
    }

    void Interpret::addFlowLayer(const ast::Node & code,
                                boundary_index_t boundary_index, 
                                std::function<void (const FlowLayerInfo &)> on_layer_end)
    {
        addFlowLayer_internal(code, boundary_index, on_layer_end);
    }

    bool Interpret::flushLayers(const ast::Node & code, bool invoke_callback)
    {
        for(size_t begin_index = _layer_stack.size()-1; begin_index < _layer_stack.size(); --begin_index)
            if (&_layer_stack[begin_index].code == &code) 
            {
                boundary_index_t boundary_index = _layer_stack[begin_index].boundary_index;
                size_t           flushed_count  = _layer_stack.size() - begin_index;
                FlowLayerInfo    flow_info      = _layer_stack[begin_index];

                for(size_t i = 0; i < flushed_count; ++i)
                    _layer_stack.pop_back();

                if (boundary_index != UNDEFINED_BOUNDARY_INDEX)
                    stack().clearBoundary(boundary_index);

                if (invoke_callback)
                    flow_info.on_layer_end(flow_info);

                return true;
            }

        return false;
    }

    void Interpret::instantiateSemantics(SemanticModules hosts)
    {
        for(auto host : hosts) 
            _hosts.push_back(host);

        for(auto host : _hosts) 
            host->setInterpret(this);
    }

    void Interpret::addFile(const std::string & path_to_file)
    {
        if (std::find(_files.begin(), _files.end(), path_to_file) == _files.end())
            _files.push_back(path_to_file);
    }   

    bool Interpret::execute(const ast::Node & code)
    {
        if (!_layer_stack.empty())
            return false;

        addFlowLayer(code, UNDEFINED_BOUNDARY_INDEX, nullptr);
        _loom.stretch(this);
        _loom.finish(); 
        return _layer_stack.empty();
    }

    loom::FiberStatus Interpret::executeOperation()
    { 
        return tieKnot(); 
    }
                                        
    const ast::Node * Interpret::lookupOperationNode(int shift, boundary_index_t bound) const
    {
        if (!isReady())
            return nullptr;

        const FlowLayerInfo * p_flow = nullptr;
        
        if (bound >= _stack.boundaries().size())
            // Если граница не задана, берём верхний, т.е. рабочий слой
            p_flow = & _layer_stack.back();
        else if (_layer_stack.front().boundary_index == bound) {
            // Если граница на первом уровне - это не штатный режим. 
            // Значит был параллельный вызов, скорее всего, вызываюшая сторона находится в другом потоке
        }
        else {
            if (_stack.boundaries()[bound].mark != BOUNDARY_MARK_FUNCTION_FRAME)
                // Корневой вызов - ищем первый вызов функции
                for (size_t ib=bound+1; ib < _stack.boundaries().size(); ++ib)
                    if (_stack.boundaries()[ib].mark == BOUNDARY_MARK_FUNCTION_FRAME) {
                        bound = ib;
                        break;
                    }

            // Если граница принадлежит функции, то берём слой под ним
            for(size_t i=1; i < _layer_stack.size(); ++i)
                if (_layer_stack[i].boundary_index == bound) {
                    p_flow = & _layer_stack[i-1];
                    break;
                }
        }

        if (!p_flow)
            return nullptr;

        size_t index = p_flow->index + shift;

        if (index >= p_flow->code.branches().size())
            return nullptr;

        return & p_flow->code.branches()[index];
    }

    boundary_index_t Interpret::copyBaseBounds(const StackOfNames_interface & another_stack)
    {
        stack().startBoundary(BoundScope::Global, BOUNDARY_MARK_MODULE_FRAME);

        auto [begin, end] = another_stack.boundaryLowAndTop(0);

        for(name_index_t i=begin; i < end; ++i)
            stack().push(another_stack.variable(i).copyVariable());

        _stack.globals() = another_stack.globals();

        return stack().startBoundary(BoundScope::Global, BOUNDARY_MARK_MODULE_FRAME);
    }

    void Interpret::addBreakPoints(const std::vector<BreakPoint> & breakpoints)
    {
        _breakpoints.insert(_breakpoints.end(), breakpoints.begin(), breakpoints.end());
    }

    std::vector<BreakPoint> Interpret::breakpoints() const
    {
        return _breakpoints;
    }

    void Interpret::addFlowLayer_internal(const ast::Node & code,
                                boundary_index_t boundary_index, 
                                std::function<void (const FlowLayerInfo &)> on_layer_end)
    {
        if (!code.branches().empty())
            _layer_stack.push_back({code,on_layer_end,boundary_index});
        else if (on_layer_end)
            on_layer_end({code,on_layer_end,boundary_index}); // Например, метод без параметров
    }


}